package com.active.fnd.arch.wizard.model
{
	import com.active.fnd.arch.application.model.NodePM;
	import com.active.fnd.arch.util.GlobalEventDispatcher;
	import com.active.fnd.arch.wizard.event.StepNavEvent;

	import mx.core.UIComponent;
	import mx.rpc.AsyncResponder;
	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;

	[Bindable]
	public class WizardPM extends NodePM
	{
		public var currentView:UIComponent;
		public var currentPM:StepPM;

		[ArrayElementType("com.active.fnd.arch.wizard.model.Step")]
		public var steps:Array=[];
		public var currentStep:Step;

		//Add to support sidebarIndex
		public var currentIndex:int;

		public function WizardPM()
		{
			GlobalEventDispatcher.getInstance().addEventListener(StepNavEvent.EVENT_TYPE, stepNavEventHandler);
			initSteps();
		}

		protected function initSteps():void
		{
			throw new Error("Method initSteps() in WizardPM must be overriden by child class!");
		}

		protected function addStep(name:String, pmClass:Class, viewClass:Class, showBottomBar:Boolean, showBack:Boolean, isContinue:Boolean):void
		{
			var step:Step=new Step(name, pmClass, viewClass, showBottomBar, showBack, isContinue);
			steps.push(step);
		}

		override public function enterNode(data:Object, responder:IResponder):void
		{
			loadWizardData(data, new AsyncResponder(loadDataSuccess, loadDataFail));
			function loadDataSuccess(result:ResultEvent, token:Object=null):void
			{
				if (data && data.stepName) //stepName as the most important parameter from outside
				{
					var destStep:Step=findStepByStepName(data.stepName);
					navigateToStep(destStep, responder);
				}
				else
				{
					navigateToStep(steps[0], responder);
				}
			}
			function loadDataFail(error:FaultEvent, token:Object=null):void
			{
				enterNodeFail(responder);
			}
		}

		override public function isDataChanged():Boolean
		{
			return currentPM.isDataChanged();
		}

		public function loadWizardData(data:Object, responder:IResponder):void
		{
			throw new Error("Method loadWizardData() in WizardPM must be overriden by child class!");
		}

		public function stepNavEventHandler(event:StepNavEvent):void
		{
			switch (event.navType)
			{
				case StepNavEvent.NAV_TO_NEXT:
					navigateToStep(findNextStep());
					break;
				case StepNavEvent.NAV_TO_PREVIOUS:
					navigateToStep(findPreviousStep());
					break;
				case StepNavEvent.NAV_TO_CUSTOM:
					navigateToStep(findStepByStepName(event.stepName));
					break;
				default:
					break;
			}
		}

		protected function findStepPosition(stepName:String):int
		{
			var result:int=-1;
			var length:int=steps.length;
			for (var i:int=0; i < length; i++)
			{
				var step:Step=steps[i];
				if (step.name == stepName)
				{
					result=i;
					break;
				}
			}
			if (result == -1)
			{
				throw new Error("Cannot find step with name '" + stepName + "'!");
			}
			return result;
		}

		protected function findStepByStepName(stepName:String):Step
		{
			return steps[findStepPosition(stepName)];
		}

		protected function findNextStep():Step
		{
			var currentPos:int=findStepPosition(currentStep.name);
			if (currentPos == steps.length - 1)
			{
				throw new Error("Cann't find next step cause currentStep is the last step!");
			}
			else
			{
				return steps[currentPos + 1];
			}
		}

		protected function findPreviousStep():Step
		{
			var currentPos:int=findStepPosition(currentStep.name);
			if (currentPos == 0)
			{
				throw new Error("Cann't find previous step cause currentStep is the first step!");
			}
			else
			{
				return steps[currentPos - 1];
			}
		}

		protected function navigateToStep(destStep:Step, enterNodeResponder:IResponder=null):void
		{
			if (destStep != currentStep)
			{
				if (currentStep == null)
				{
					enterDestStep(destStep, enterNodeResponder);
				}
				else
				{
					currentIndex=findStepPosition(destStep.name);
					trace("currentIndex = " + currentIndex + "!");
					currentPM.exitStep(new AsyncResponder(exitSuccess, exitFail));
					function exitSuccess(result:Object, token:Object=null):void
					{
						trace("Exit step " + currentStep.name + " success, currentIndex = " + currentIndex + "!");
						enterDestStep(destStep, enterNodeResponder);
					}

					function exitFail(error:FaultEvent, token:Object=null):void
					{
						enterNodeFail(enterNodeResponder);
						currentIndex=findStepPosition(currentStep.name);
						trace("Exit step " + currentStep.name + " fail, currentIndex = " + currentIndex + "!");
					}
				}
			}
			else
			{
				enterNodeFail(enterNodeResponder);
			}
		}

		public function navToIndex(newIndex:int):void
		{
			navigateToStep(steps[newIndex]);
		}

		private function enterNodeSuccess(enterNodeResponder:IResponder):void
		{
			if (enterNodeResponder)
			{
				enterNodeResponder.result(null);
			}
		}

		private function enterNodeFail(enterNodeResponder:IResponder):void
		{
			if (enterNodeResponder)
			{
				enterNodeResponder.fault(null);
			}
		}

		protected function enterDestStep(destStep:Step, enterNodeResponder:IResponder):void
		{
			var destStepPM:StepPM=destStep.createPM();
			destStepPM.enterStep(null, new AsyncResponder(enterSuccess, enterFail));
			trace("enter step " + currentPM.pmId() + "!");
			function enterSuccess(result:Object, token:Object=null):void
			{
				selectStep(destStep, destStepPM);
				trace("Enter step " + currentStep.name + " success!");
				enterNodeSuccess(enterNodeResponder);
			}

			function enterFail(error:FaultEvent, token:Object=null):void
			{
				trace("Enter step " + currentStep.name + " fail!");
				enterNodeFail(enterNodeResponder);
			}
		}

		protected function selectStep(destStep:Step, destStepPM:StepPM):void
		{
			//Clear old pm
			if (currentPM)
			{
				currentPM.removeFromPMContext();
			}
			currentIndex=findStepPosition(destStep.name);
			currentStep=destStep;
			currentPM=destStepPM;

			currentView=currentStep.createView();
		}

		public function saveAndBack():void
		{
			currentPM.saveChange(new AsyncResponder(saveSuccess, saveFail));
			function saveSuccess(result:Object, token:Object=null):void
			{
				trace("Save stepPM " + currentPM.pmId() + " success!");
				if (steps[0] != currentStep)
				{
					stepNavEventHandler(new StepNavEvent(StepNavEvent.NAV_TO_PREVIOUS));
				}
			}

			function saveFail(error:FaultEvent, token:Object=null):void
			{
				trace("Exit stepPM " + currentPM.pmId() + " fail!");
			}
		}

		public function saveAndContinue():void
		{
			currentPM.saveChange(new AsyncResponder(saveSuccess, saveFail));
			function saveSuccess(result:Object, token:Object=null):void
			{
				trace("Save stepPM " + currentPM.pmId() + " success!");
				if (steps[steps.length - 1] != currentStep)
				{
					stepNavEventHandler(new StepNavEvent(StepNavEvent.NAV_TO_NEXT));
				}
			}

			function saveFail(error:FaultEvent, token:Object=null):void
			{
				trace("Exit stepPM " + currentPM.pmId() + " fail!");
			}
		}
	}
}